#include "pdePlan.h"
#include <fstream>
#include "bson_helper.h"
#include "bson_helper.h"

namespace pde {

	std::shared_ptr<OptFunc> OptFunc::New() {
		return std::make_shared<OptFunc>();
	}
	OptFunc::OptFunc() {
		rbe_dose_ = 0;
		roi_number_ = 0;
		roi_type_ = 0;
		func_type_ = 0;
		weight_ = 0;
		volume_ = 0;
		dose_ = 0;
	}

	OptFunc::~OptFunc() {

	}

	std::string OptFunc::to_bson() {
		auto doc = document{};
		to_bson_value("roi_number", roi_number_);
		to_bson_value("roi_type", roi_type_);
		to_bson_value("func_type", func_type_);
		to_bson_value("rbe_dose", rbe_dose_);
		to_bson_value("weight", weight_);
		to_bson_value("volume", volume_);
		to_bson_value("dose", dose_);
		return std::string((char*)doc.view().data(), doc.view().length());

	}
	bool OptFunc::from_bson(const std::string& bson_str) {
		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_value_int32("roi_number", roi_number_);
		from_bson_value_int32("roi_type", roi_type_);
		from_bson_value_int32("func_type", func_type_);
		from_bson_value_int32("rbe_dose", rbe_dose_);
		from_bson_value_double("weight", weight_);
		from_bson_value_double("volume", volume_);
		from_bson_value_double("dose", dose_);
		return true;
	}
	//------------------------------------------------------------------------------------------------------

	std::shared_ptr<Spot> Spot::New() {
		return std::make_shared<Spot>();
	}
	Spot::Spot() {

		mu_ = 0;
		dose_model_ = nullptr;
	}
	Spot::~Spot() {

	}

	void Spot::set_position(double x, double y, double z) {
		position_[0] = x;
		position_[1] = y;
		position_[2] = z;
	}
	std::string Spot::to_bson() {

		auto doc = document{};
		to_bson_value("mu", mu_);
		to_bson_array("position", position_, 3);
		return std::string((char*)doc.view().data(), doc.view().length());
	}
	bool Spot::from_bson(const std::string& bson_str) {

		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_value_double("mu", mu_);
		from_bson_array_double("position", position_, 3);
		return true;
	}

	//------------------------------------------------------------------------------------------------------
	std::shared_ptr<Layer> Layer::New() {

		return std::make_shared<Layer>();
	}

	Layer::Layer() {
		range_shift_ = 0;
		energy_ = 0;
	}

	Layer::~Layer() {

	}

	std::string Layer::to_bson() {

		auto doc = document{};
		to_bson_value("range_shift", range_shift_);
		to_bson_value("energy", energy_);
		to_bson_obj_list("spot_list", spot_list_);
		return std::string((char*)doc.view().data(), doc.view().length());

	}
	bool Layer::from_bson(const std::string& bson_str) {

		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_value_double("range_shift", range_shift_);
		from_bson_value_double("energy", energy_);
		from_bson_obj_list("spot_list", spot_list_, Spot);
		return true;
	}


	//------------------------------------------------------------------------------------------------------

	std::shared_ptr<Beam> Beam::New() {

		return std::make_shared<Beam>();
	}
	Beam::Beam() {
		gantry_angle_ = 0;
		couch_angle_ = 0;
		weight_ = 0;
		nozzle_id_ = 0;

	}
	Beam::~Beam() {

	}

	std::string Beam::to_bson() {
		auto doc = document{};
		to_bson_array("iso_center", iso_center_, 3);
		to_bson_array("snout_innter_range", snout_innter_range_, 2);
		to_bson_value("gantry_angle", gantry_angle_);
		to_bson_value("snout_tip_distance_to_iso", snout_tip_distance_to_iso_);
		to_bson_value("couch_angle", couch_angle_);
		to_bson_value("weight", weight_);
		to_bson_value("nozzle_id", nozzle_id_);
		to_bson_obj_list("layer_list", layer_list_);
		return std::string((char*)doc.view().data(), doc.view().length());

	}
	bool Beam::from_bson(const std::string& bson_str) {
		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_array_double("iso_center", iso_center_, 3);
		from_bson_array_double("snout_inner_range", snout_innter_range_, 2);

		from_bson_value_double("snout_tip_distance_to_iso", snout_tip_distance_to_iso_);
		from_bson_value_double("gantry_angle", gantry_angle_);
		from_bson_value_double("couch_angle", couch_angle_);
		from_bson_value_double("weight", weight_);
		from_bson_value_int32("nozzle_id", nozzle_id_);
		from_bson_obj_list("layer_list", layer_list_, Layer);
		return true;
	}
	//------------------------------------------------------------------------------------------------------
	std::shared_ptr<Plan> Plan::New() {

		return std::make_shared<Plan>();
	}
	Plan::Plan() {

	}
	Plan::~Plan() {

	}

	std::string Plan::to_bson() {
		auto doc = document{};
		to_bson_array("beam_margins", beam_margins_, 4);
		to_bson_value("repaint_count", repaint_count_);
		to_bson_value("robust_opt", robust_opt_);
		to_bson_value("robust_tol", robust_tol_);
		to_bson_value("opt_max_iter_count", opt_max_iter_count_);
		to_bson_value("opt_limit_time", opt_limit_time_);

		to_bson_obj_list("beam_list", beam_list_);
		to_bson_obj_list("opt_func_list", opt_func_list_);
		return std::string((char*)doc.view().data(), doc.view().length());
	}
	bool Plan::from_bson(const std::string& bson_str) {
		auto bson = view(reinterpret_cast<const uint8_t*>(bson_str.c_str()), bson_str.length());
		from_bson_array_double("beam_margins", beam_margins_, 4);
		from_bson_value_int32("repaint_count", repaint_count_);
		from_bson_value_int32("robust_opt", robust_opt_);
		from_bson_value_double("robust_tol", robust_tol_);
		from_bson_value_int32("opt_max_iter_count", opt_max_iter_count_);
		from_bson_value_int32("opt_limit_time", opt_limit_time_);

		from_bson_obj_list("beam_list", beam_list_, Beam);
		from_bson_obj_list("opt_func_list", opt_func_list_, OptFunc);
		return true;
	}
	bool Plan::to_file(const std::string& fn) {
		std::ofstream ofile(fn, std::ios::binary);
		if (!ofile.good())
			return false; 
		auto& data = this->to_bson();

		ofile.write(data.c_str(), data.size());
		ofile.close();
		return true;
	}

	bool Plan::from_file(const std::string& fn) {

		std::ifstream infile(fn, std::ifstream::binary | std::ios::ate);
		if (infile.fail()) {
			return false;
		}
		std::string data;
		size_t size = infile.tellg();
		infile.seekg(0);
		data.resize(size);
		infile.read(&data[0], size);
		infile.close();

		return this->from_bson(data);
	}

	bool Plan::load_dose_models(std::shared_ptr<Nozzle> nozzle) {
		for (auto beam:beam_list_) {
			for (auto layer : beam->get_layer_list()) {
				auto energy = layer->get_energy();
				auto dm = nozzle->get_dose_model(energy);
				if (dm) {
					for (auto sp : layer->get_spot_list()) {
						sp->set_dose_model(dm);
					}
				}
				else {
					printf("Can not find dose model in nozzle for beam\n");
				}
			}

		}
		return true;
	}
}